import os
import sys
import time

import django.db.models
import pytest
from django.shortcuts import render
from apps.project.models import ProjectInfo
from apps.testcase.models import ApiCase, UiCase, UiCaseStep
from apps.runcases.models import ProjectResult
from apps.users.models import User
from django.http import JsonResponse
from apps.client.models import ClientInfo
from apps.elements.models import UIElementes, UIAction
from CloudPlatform import settings
from pathlib import Path
import threading
from multiprocessing.pool import ThreadPool
# Create your views here.


project_tmp_path = settings.BASE_DIR / 'project_tmp'
report_path = settings.BASE_DIR / 'case_reports'
case_logs_path = settings.BASE_DIR / 'case_logs'


def start_project(case_type, project_name):
    project_path = project_tmp_path / case_type / project_name
    if os.path.exists(project_path):
        os.remove(project_path)
    os.mkdir(project_path)
    with open(project_path / '__init__.py', 'w', encoding='UTF-8') as f:
        pass
    with open(project_path / 'config.py', 'w', encoding='UTF-8') as f:
        f.write("""class RunConfig:
    driver = None
    API_RESULT_CODE = None
    API_RESULT = None""")
    os.mkdir(project_path / 'tests')
    if case_type.upper() == 'UI':
        html = """<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:304px;height:228px;"
                            onclick="const img = new Image()
                                    img.src = 'data:image/png;base64,%s'
                                    const newWin = window.open('', '_blank')
                                    newWin.document.write(img.outerHTML)
                                    newWin.document.close()"
                            align="right"/></div>"""
        html_table = html_table_row = meta_data = ''
        runtest_makereport = f"""pytest_html = item.config.pluginmanager.getplugin('html')
    outcome = yield
    report = outcome.get_result()
    report.description = description_html(item.function.__doc__)
    extra = getattr(report, 'extra', [])
    if report.when == 'call' or report.when == 'setup':
        xfail = hasattr(report, 'wasxfail')
        if (report.skipped and xfail) or (report.failed and not xfail):

            img_base64 = capture_screenshots()
            html = \"\"\"{html}\"\"\" % (img_base64, img_base64)
            extra.append(pytest_html.extras.html(html))
        report.extra = extra"""
        img = """def capture_screenshots():
    global driver
    base64_img = driver.get_screenshot_as_base64()
    return base64_img"""
    else:
        html_table = """cells.insert(3, html.th('响应状态码'))
    cells.insert(4, html.th('响应内容'))"""
        html_table_row = """cells.insert(3, html.td(report.code))
    cells.insert(4, html.td(report.body))"""
        meta_data = """config._metadata['Platform'] = f'Requests-{requests.__version__}'"""
        runtest_makereport = """outcome = yield
    report = outcome.get_result()
    report.description = description_html(item.function.__doc__)
    report.code = RunConfig.API_RESULT_CODE
    report.body = RunConfig.API_RESULT"""
        img = ""

    meta_str = f"""# coding=utf-8
import os
import time
import requests
import pytest
from py.xml import html
from selenium import webdriver
from selenium.webdriver import Remote
from .config import RunConfig
from loguru import logger

logger.add(r"{case_logs_path / f'{project_name}.log'}", encoding="utf-8", retention="7 days", backtrace=True, enqueue=True)

report_path = r"{report_path}"

def pytest_html_report_title(report):
        report.title = "{case_type.upper()}自动化测试报告"

def pytest_html_results_table_header(cells):
    cells.insert(2, html.th('用例描述'))
    {html_table}
    cells.pop()
    
def pytest_html_results_table_row(report, cells):
    cells.insert(2, html.td(report.description))
    {html_table_row}
    cells.pop()
    
def pytest_configure(config):
    config._metadata.pop("JAVA_HOME")
    config._metadata.pop("Plugins")
    config._metadata['开始时间'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    config._metadata['测试人'] = '张泽旺'
    {meta_data}
    
def pytest_runtest_call(item):
    logger.info('开始执行用例-%s' % item.function.__name__.split("test_")[-1])
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item):
    {runtest_makereport}

def description_html(desc):
    if desc is None:
        return "No case description"
    desc_ = ""
    for i in range(len(desc)):
        if i == 0:
            pass
        elif desc[i] == '\\n':
            desc_ = desc_ + ";"
        else:
            desc_ = desc_ + desc[i]

    desc_lines = desc_.split(";")
    desc_html = html.html(
        html.head(
            html.meta(name="Content-Type", value="text/html; charset=latin1")),
        html.body(
            [html.p(line) for line in desc_lines]))
    return desc_html
{img}
"""
    with open(project_path / 'conftest.py', 'w', encoding='utf-8') as f:
        f.write(meta_str)


def write_fixture(case_type, project_name, data: str):
    project_path = project_tmp_path / case_type / project_name
    with open(project_path / 'conftest.py', 'a+', encoding='utf-8') as f:
        f.write('\n')
        f.write(data)


def write_case(case_type, project_id, project_name, process_num=1):
    project_path = project_tmp_path / case_type / project_name
    case_file = project_path / 'tests' / f'test_{project_name}.py'
    with open(project_path / 'tests' / '__init__.py', 'w', encoding='UTF-8') as f:
        pass
    if case_type.lower() == 'ui':
        case_infos = UiCase.objects.filter(project_id__contains=f'{project_id};')
        if process_num > 1:
            case_count = case_infos.count()
            case_part_num = case_count // process_num
            offset = int(project_name.split('___')[0])
            if offset != process_num:
                case_part_data = case_infos[(offset - 1) * case_part_num:offset * case_part_num - 1]
            else:
                case_part_data = case_infos[(offset - 1) * case_part_num:]
            write_case_file(case_file, case_part_data)
        else:
            write_case_file(case_file, case_infos)


def write_case_file(file_path, data: django.db.models.QuerySet):
    file_data = """# coding=utf-8
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.action_chains import ActionChains
from ..conftest import logger\n"""
    new = ''
    for case in data:
        new = f"""def test_{case.title}(driver):
    \"\"\"{case.desc}\"\"\"
    """
        id = case.id
        step_list = UiCaseStep.objects.filter(case_id=id).order_by('index')
        for step in step_list:
            el = step.element
            new = new + f"""el = driver.find_element(by=By.{el.by.upper()}, value=\"{el.value}\")\n"""
            action = step.action
            if action.value.lower() == 'check':
                if step.element:
                    if step.attr.lower() == 'text':
                        new = new + '\n' + f"""    assert el.text == \"{step.value}\""""
                    else:
                        new = new + '\n' + f"""    assert el.get_property(\"{step.attr}\")==\"{step.value}\""""
            else:
                if step.timeout:
                    new = new + f"""    try:
        WebDriverWait(driver, {step.timeout}).until(expected_conditions.visibility_of(el))
    except Exception as e:
        assert False
    """
                if action.module == 'webdriver':
                    if step.value:
                        new = new + '\n' + f"""    el.{action.value}(\"{step.value}\")"""
                        new = new + '\n' + f"""    logger.info("✅ {action.name}元素--{el.page.name}-{el.name}-{step.value}成功！")"""
                    else:
                        new = new + '\n' + f"""    el.{action.value}"""
                        new = new + '\n' + f"""    logger.info("✅ {action.name}元素--{el.page.name}-{el.name}成功！")"""
                else:
                    if step.value:
                        new = new + '\n' + f"""    ActionChains(driver).{action.value}(el, \"{step.value}\").perform()"""
                        new = new + '\n' + f"""    logger.info("✅ {action.name}元素--{el.page.name}-{el.name}-{step.value}成功！")"""
                    else:
                        new = new + '\n' + f"""    ActionChains(driver).{action.value}(el).perform()"""
                        new = new + '\n' + f"""    logger.info("✅ {action.name}元素--{el.page.name}-{el.name}成功！")"""
    file_data = file_data + new
    with open(file_path, 'w', encoding='UTF-8') as f:
        f.write(file_data)


def write_contest(case_type, client_info, project_info, project_name, url):
    # ------------写入全局的contest.py文件------------ #
    if case_type.upper() == 'UI':
        # ------------写入浏览器驱动------------ #
        client_ip = client_info.ip
        client_port = client_info.port
        client_type = client_info.sys_type
        if project_info[0].env_browser.lower() == 'chrome':
            options = f"""options = webdriver.ChromeOptions()"""
        elif project_info[0].env_browser.lower() == 'firefox':
            options = f"""options = webdriver.FirefoxOptions()"""
        elif project_info[0].env_browser.lower() == 'ie':
            options = f"""options = webdriver.IeOptions()"""
        elif project_info[0].env_browser.lower() == 'edge':
            options = f"""options = webdriver.EdgeOptions()"""
        else:
            return JsonResponse({'status': 204, 'msg': f'当前浏览器-{project_info[0].env_browser}不受支持，请选择其他'
                                                       f'浏览器！'}, status=204)
        if client_type == 'Linux':
            arg = """    options.add_argument("--headless")
    options.add_argument('--disable-gpu')
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')"""
        else:
            arg = ''
        options = options + '\n' + arg
        driver = f"""@pytest.fixture(scope='session', autouse=True)
def driver():
    global driver
    {options}
    driver = Remote(command_executor='http://{client_ip}:{client_port}/wd/hub',
                    options=options)
    driver.maximize_window()
    driver.implicitly_wait({project_info[0].timeout})
    RunConfig.driver=driver
    driver.get(\"{url}\")
    return driver"""
        write_fixture(case_type.upper(), project_name, driver)
        close_driver = """@pytest.fixture(scope="session", autouse=True)
def close():
    yield driver
    driver.quit()
    logger.info("test end!")"""
        write_fixture(case_type.upper(), project_name, close_driver)


def run(request):
    if request.method == 'POST':
        # ------------拉取运行项目环境------------ #
        user_id = request.session.get('_auth_user_id')
        user_name = User.objects.filter(id=user_id).first().username
        project_id = request.POST.get('project_id', None)
        if not project_id:
            return JsonResponse({'status': 401, 'msg': '请求参数错误，请重试！'}, status=401)
        project_info = ProjectInfo.objects.filter(id=project_id)
        if not project_info:
            return JsonResponse({'status': 402, 'msg': '项目不存在！'}, status=402)
        # 运行项目名称，区别于项目名称, 默认为项目名称+启动时间
        run_time = request.POST.get('time', None)
        project_name = project_info[0].name + '__' + run_time
        if not all([project_name, run_time]):
            return JsonResponse({'status': 401, 'msg': '请求参数错误，请重试！'}, status=401)
        client_info = project_info[0].env_client
        if not client_info:
            return JsonResponse({'status': 401, 'msg': '请求客户端不存在，请重试！'}, status=401)
        if client_info.isactive == 0:
            return JsonResponse({'status': 203, 'msg': '客户端未激活，请激活客户端后重新启动！'}, status=203)
        process_num = int(project_info[0].process_num)
        case_type = project_info[0].type
        # ------------新建临时项目------------ #
        start_pool = ThreadPool(process_num)
        if process_num == 1:
            start_project(case_type.upper(), project_name)
            write_contest(case_type, client_info, project_info, project_name, project_info[0].url)
            write_case(case_type.upper(), project_id, project_name, process_num)
            start_pool.apply_async(start_case, args=(case_type.upper(), project_name, process_num, run_time
                                                     ,client_info, user_name), callback=call_start_case)
        else:
            for i in range(process_num):
                start_project(case_type.upper(), project_name + f'___{i}')
                write_contest(case_type, client_info, project_info, project_name)
                # ------------写入用例------------ #
                write_case(case_type.upper(), project_id, project_name, process_num)
                start_pool.apply_async(start_case, args=(case_type.upper(), project_name, process_num, run_time,
                                                         client_info, user_name), callback=call_start_case)
        # ########################运行####################
        start_pool.close()
        return JsonResponse({'status': 200, 'msg': '运行成功！'}, status=200)
    else:
        return JsonResponse({'status': 403, 'msg': '请求非法，请重试！'}, status=403)


def start_case(case_type, project_name, process_num, run_time, client_info, user_name):
    # id = threading.current_thread()
    project_path = project_tmp_path / case_type / project_name
    pytest.main(["-s", "-v", project_path / 'tests',
                 "--html=" + report_path / project_name + '.html',
                 # "--junit-xml=" + xml_report,
                 "--self-contained-html",
                 # "--maxfail", RunConfig.max_fail,
                 # "--reruns", RunConfig.rerun,
                 '--capture=fd'])
    return {'result': f'{project_name}运行完成！', 'project': project_name, 'process_num': process_num,
            'time': run_time, 'client': client_info, 'user': user_name}


def call_start_case(result):
    if result['process_num'] ==1:
        data = {
            'project_name': result['project'].split('__')[0],
            'start_time': result['time'],
            'client': result['client_info'].name,
            'process_num': result['process_num'],
            'report': result['project']+'.html;',
            'user': result['user'],
            'is_end': True
        }
        project_result = ProjectResult.objects.create(**data)
        project_result.save()
    else:
        old = ProjectResult.objects.filter(project_name=result['project'].split('__')[0], start_time=result['time'])
        if old:
            if len(old.first().report.split(';')) == result['proecss_num'] -1:
                old.first().is_end = True
            old.first().report = old.first().report + result['project'] + '.html;'
            old.first().save()
        else:
            data = {
                'project_name': result['project'].split('__')[0],
                'start_time': result['time'],
                'client': result['client_info'].name,
                'process_num': result['process_num'],
                'report': result['project'] + '.html;',
                'user': result['user'],
                'is_end': False,
            }
            project_result = ProjectResult.objects.create(**data)
            project_result.save()


if __name__ == '__main__':
    thread_id = []
    def start_casew(thread_id: list):
        print('1221')
        # time.sleep(10)
        id = threading.current_thread()
        print(id)
        thread_id.append(id)
    start_pool = ThreadPool(8)
    for i in range(9):
        start_pool.apply_async(start_casew, args=(thread_id,))
    start_pool.close()
    start_pool.join()
    print(thread_id)
